/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName    :  PanoramicBondDealContainer.cpp
 *Author    :  shangyi.xin
 *Version    :  1.0
 *Date        :  2019/03/06
 *Desc        :  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others    :  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History    :
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#define NOMINMAX
#include "PanoramicBondDealContainer.h"

#include <core/time/date.h>
#include <core/time/system_time.h>

#include "../BondCDCPriceInfo.h"
#include "../BondCalendar.h"
#include "../BondContainer.h"
#include "../BondHisMarketStream.h"
#include "../BondInfo.h"
#include "../SSVContainer.h"

namespace {
#define DOUBLE_INVALID -1e7
#define DOUBLE_ZERO 1e-6
#define CHECK_DOUBLE_VALID(v) v > (DOUBLE_INVALID + 1e-5)
}  // namespace

CPanoramicBondDealContainer& CPanoramicBondDealContainer::instance() {
  static CPanoramicBondDealContainer s_bc;
  return s_bc;
}

CPanoramicBondDealContainer::CPanoramicBondDealContainer()
    : m_bGetData(false), m_isHistory(false) {}

void CPanoramicBondDealContainer::SetReqData() {
  SCOPED_LOCK(m_lock);
  m_bGetData = true;
}

bool CPanoramicBondDealContainer::GetReqData() {
  SCOPED_LOCK(m_lock);
  return m_bGetData;
}

bool CPanoramicBondDealContainer::IsContainerEmpty() {
  return m_bondDeal.isEmpty();
}

void CPanoramicBondDealContainer::Clear() {
  SCOPED_LOCK(m_lock);
  m_bondDeal.clear();
}

void CPanoramicBondDealContainer::UpdateBondDeal(
    xQBPanoramicMarketStreamDealAck* pData) {
  SCOPED_LOCK(m_lock);
  // 插入数据至容器,复杂度n*n, 没有做索引
  if (!pData) return;
  // 插入数据时进行计算
  sPanoramicBondDealUnit containerUnit;
  std::list<xQBPanoramicMarketStreamUnit>::iterator it = pData->m_List.begin();
  int nInvalidateDay = GetBondDealInvalidateDay();  // 性能优化
  CBondContainer& bc = CBondContainer::instance();
  for (; it != pData->m_List.end(); ++it) {
    // GetEstPrice  GetEstCleanPrice
    const xQBPanoramicMarketStreamUnit& unit = *it;
    if (qb::base::IntDate::timeToInt(unit.m_updateTime) != nInvalidateDay)
      continue;
    int bondIndex = bc.GetBondIndex(unit.m_bondkey, unit.m_listedmarket);
    if (!bc.IsValidIndex(bondIndex)) continue;

    containerUnit = unit;
    double dEstPrice = DOUBLE_INVALID;
    double dEstCleanPrice = DOUBLE_INVALID;
    double dPreCloseYield = GetDoubleValue(unit.m_preCloseYield);
    double dPreCloseCleanPrice = GetDoubleValue(unit.m_preClosecleanPrice);
    double dDealYield = GetDoubleValue(unit.m_yield);
    double dDealCleanPrice = GetDoubleValue(unit.m_cleanPrice);
    int nBondType = panoramic::bondType_null;
    {
      const CBondInfo& bi = bc.ElementAtR(bondIndex);
      GetEstimatePrice(dEstPrice, dEstCleanPrice, dDealYield, dDealCleanPrice,
                       bondIndex);

      if (bi.IsBondNCD()) {
        nBondType = panoramic::bondType_ncd;
      } else if (bi.is_interest_bond()) {
        nBondType = panoramic::bondType_interest;
      } else if (bi.is_credit_bond()) {
        nBondType = panoramic::bondType_credit;
      }
      FIELDCOPY(containerUnit.m_OptionType, bi.GetOptionType());
    }
    containerUnit.m_cdcGapPercent =
        GetDoublePercent(dDealCleanPrice, dEstCleanPrice);
    containerUnit.m_cdcGapCleanPrice =
        GetDoubleBP(dDealCleanPrice, dEstCleanPrice);
    containerUnit.m_cdcGapBP = GetDoubleBP(dDealYield, dEstPrice);
    containerUnit.m_closeGapPercent =
        GetDoublePercent(dDealCleanPrice, dPreCloseCleanPrice);
    containerUnit.m_closeGapBP = GetDoubleBP(dDealYield, dPreCloseYield);

    // 插入container的数据按照bondkey排序
    auto& dealInfo = m_bondDeal.getBondDeal(nBondType);

    bool bInput = false;
    auto itDeal = dealInfo.find(bondIndex);
    if (itDeal == dealInfo.end()) {
      dealInfo.insert(std::make_pair(bondIndex, BondDealInfo()));
      itDeal = dealInfo.find(bondIndex);
    }
    if (itDeal == dealInfo.end()) continue;  // 非法错误
    itDeal->second.m_cdcValue = dEstPrice;
    FIELDCOPY(itDeal->second.m_lastClose, containerUnit.m_preCloseYield);
    itDeal->second.insert(containerUnit);
    //
    // 		for (auto itDeal : *pDeal)
    // 		{
    // 		}
    // 		for (; itList != listDeal.m_List.end(); ++itList)
    // 		{
    // 			int nCompareResultKey = strcmp(containerUnit.m_bondkey,
    // itList->m_bondkey); 			if (nCompareResultKey < 0){ 				if
    // (VerifyDealValid(containerUnit)){ 					itList = listDeal.m_List.insert(itList,
    // containerUnit); 					dealCount++; 					updateTime = containerUnit.m_updateTime;
    // 				}
    // 				bInput = true;
    // 				break;
    // 			}
    // 			else if (nCompareResultKey == 0){
    // 				int nCompareResultMarket =
    // strcmp(containerUnit.m_listedmarket, itList->m_listedmarket); 				if
    // (nCompareResultMarket < 0){ 					if (VerifyDealValid(containerUnit)){ 						itList =
    // listDeal.m_List.insert(itList, containerUnit); 						dealCount++; 						updateTime =
    // containerUnit.m_updateTime;
    // 					}
    // 					bInput = true;
    // 					break;
    // 				}
    // 				else if (nCompareResultMarket == 0){
    // 					//此时说明券完全匹配，需要继续判断id
    // 					//券相同持续判断了id,这里可能可以做优化
    // 					if (strcmp(containerUnit.m_id,
    // itList->m_id) == 0){ 						int nDealStatus = atoi(containerUnit.m_dealStatus);
    // 						int messBegin =
    // itList->m_messBegin; 						if (nDealStatus >= 4){ 							itList =
    // listDeal.m_List.erase(itList); 							dealCount--;
    // 							////updateTime =
    // containerUnit.m_updateTime; 							bInput = true; 							if (messBegin == 0 && itList
    // != listDeal.m_List.end()){ 								itList->m_messBegin = 0; //将下一个置为0
    // 							}
    // 							break;
    // 						}
    // 						else
    // 						{
    // 							*itList = containerUnit;
    // 							itList->m_messBegin =
    // messBegin; 							bInput = true; 							updateTime = containerUnit.m_updateTime; 							break;
    // 						}
    // 					}
    // 					else
    // 					{
    // 						containerUnit.m_messBegin = 1;
    // //说明已经不属于bond序列头部了
    // 					}
    // 				}
    // 			}
    // 		}
    // 		if (!bInput){
    // 			if (VerifyDealValid(containerUnit)){
    // 				listDeal.m_List.push_back(containerUnit);
    // 				dealCount++;
    // 				updateTime = containerUnit.m_updateTime;
    // 			}
    // 		}
  }
}

void CPanoramicBondDealContainer::ReCalculaterAllCDC() {
  SCOPED_LOCK(m_lock);

  for (int type = panoramic::bondType_interest; type <= panoramic::bondType_ncd;
       type++) {
    auto& dealMap = m_bondDeal.getBondDeal(type);
    for (auto& itInfo : dealMap) {
      for (auto& itDeal : itInfo.second.m_tradeList) {
        ReCalcuCDC(itDeal);
      }
    }
  }
}

bool CPanoramicBondDealContainer::ReCalcuCDC(sPanoramicBondDealUnit& cdcUnit) {
  double dEstPrice = DOUBLE_INVALID;
  double dEstCleanPrice = DOUBLE_INVALID;
  double dDealYield = GetDoubleValue(cdcUnit.m_yield);
  double dDealCleanPrice = GetDoubleValue(cdcUnit.m_cleanPrice);
  CBondContainer& bc = CBondContainer::instance();
  int bondIndex = bc.GetBondIndex(cdcUnit.m_bondkey, cdcUnit.m_listedmarket);
  if (!bc.IsValidIndex(bondIndex)) return false;
  const CBondInfo& bi = bc.ElementAtR(bondIndex);
  GetEstimatePrice(dEstPrice, dEstCleanPrice, dDealYield, dDealCleanPrice,
                   bondIndex);
  cdcUnit.m_cdcGapPercent = GetDoublePercent(dDealCleanPrice, dEstCleanPrice);
  cdcUnit.m_cdcGapCleanPrice = GetDoubleBP(dDealCleanPrice, dEstCleanPrice);
  cdcUnit.m_cdcGapBP = GetDoubleBP(dDealYield, dEstPrice);
  FIELDCOPY(cdcUnit.m_OptionType, bi.GetOptionType());
  return true;
}

bool CPanoramicBondDealContainer::VerifyDirection(const double& dLeft,
                                                  const double& dRight) {
  if (dLeft > DOUBLE_ZERO && dRight > DOUBLE_ZERO) return true;  // 同向
  if (dLeft < (-DOUBLE_ZERO) && dRight < (-DOUBLE_ZERO)) return true;
  return false;
}

void CPanoramicBondDealContainer::GetDisChartData(int nType, time_t tNow,
                                                  CDealDisChartList& data,
                                                  bool eng) {}

double CPanoramicBondDealContainer::GetDoubleValue(const char* pValue) {
  if (!pValue) return DOUBLE_INVALID;
  if (!IsStringEmpty(pValue)) return atof(pValue);
  return DOUBLE_INVALID;
}

double CPanoramicBondDealContainer::GetDoubleBP(const double& dValue1,
                                                const double& dValue2) {
  if (CHECK_DOUBLE_VALID(dValue1) && CHECK_DOUBLE_VALID(dValue2))
    return 100 * (dValue1 - dValue2);
  return DOUBLE_INVALID;
}

double CPanoramicBondDealContainer::GetDoublePercent(const double& dValue1,
                                                     const double& dValue2) {
  if (CHECK_DOUBLE_VALID(dValue1) && CHECK_DOUBLE_VALID(dValue2) &&
      abs(dValue2) > 0.0001) {
    return 100 * (dValue1 - dValue2) / dValue2;
  }
  return DOUBLE_INVALID;
}

int CPanoramicBondDealContainer::VerifyChangeType(
    const int nType, const sPanoramicBondDealUnit& unit) {
  int nIndex = -1;
  double dValue = DOUBLE_INVALID;
  switch (nType) {
    case panoramic::kCdcPercent:
      dValue = unit.m_cdcGapPercent;
      break;
    case panoramic::kCdcBP:
      dValue = unit.m_cdcGapBP;
      break;
    case panoramic::kClosePercent:
      dValue = unit.m_closeGapPercent;
      break;
    case panoramic::kCloseBP:
      dValue = unit.m_closeGapBP;
      break;
    default:
      break;
  }
  if (!(CHECK_DOUBLE_VALID(dValue))) return nIndex;
  static int JudgeLevelPercent[] = {10, 5, 3, 1, 0, 0, -1, -3, -5, -10};
  static int JudgeLevelBP[] = {20, 15, 10, 5, 0, 0, -5, -10, -15, -20};
  static int LvelCount = 10;
  int* pLevel = nullptr;
  if (nType % 2 == 0) {
    pLevel = JudgeLevelPercent;
  } else {
    pLevel = JudgeLevelBP;
  }
  // 首先判断为0的特殊情况
  if (abs(dValue) < DOUBLE_ZERO) {
    nIndex = 5;
  } else {
    for (int i = 0; i < LvelCount; ++i) {
      int nScale = *(pLevel + i);
      if (nScale > 0) {
        if (dValue > nScale && abs(dValue - nScale) > DOUBLE_ZERO) {
          nIndex = i;
          break;
        }
      } else {
        if (dValue > nScale || (abs(dValue - nScale) < DOUBLE_ZERO)) {
          nIndex = i;
          break;
        }
      }
    }
  }
  if (nIndex == -1) {
    nIndex = 10;  // 也需要index反向处理
  }

  nIndex = 10 - nIndex;  // 取反
  return nIndex;
}

int CPanoramicBondDealContainer::VerifyDealDisType(const int nType,
                                                   CBondInfo* pBondInfo) {
  int nBondType = -1;
  if (!pBondInfo) return nBondType;

  // 国开，非国开，国债，地方债  利率债类型
  // AAA+, AAA, AA+, AA及以下 信用债类型
  // 国有/股份制， AAA, AA+ AA及以下 NCD类型
  switch (nType) {
    case panoramic::bondType_interest: {
      if (pBondInfo->IsBondGKZ()) {
        nBondType = 2;
      } else if (strcmp(pBondInfo->GetBondSubType(), "PSB") != 0 &&
                 (strcmp(pBondInfo->GetIssuerCode(), "Z000189") == 0 ||
                  strcmp(pBondInfo->GetIssuerCode(), "Z000207") == 0)) {
        nBondType = 3;
      } else if (pBondInfo->IsBondCountry()) {
        nBondType = 4;
      } else if (pBondInfo->IsBondLocal()) {
        nBondType = 5;
      }
      break;
    }
    case panoramic::bondType_credit: {
      const char* pIssuerRating = pBondInfo->GetIssuerRatingEx();
      if (!pIssuerRating) break;
      if (strcmp(pIssuerRating, "AAA+") == 0) {
        nBondType = 0;
      } else if (strcmp(pIssuerRating, "AAA") == 0) {
        nBondType = 1;
      } else if (strcmp(pIssuerRating, "AA+") == 0) {
        nBondType = 2;
      } else {
        nBondType = 3;
      }
      break;
    }
    case panoramic::bondType_ncd: {
      const char* pIssuerRating = pBondInfo->GetIssuerRating();
      if (!pIssuerRating) break;
      if (strcmp(pBondInfo->GetBondSubType(), "MCD") == 0 ||
          strcmp(pBondInfo->GetBondSubType(), "SHD") == 0) {
        nBondType = 0;
      } else if (strcmp(pIssuerRating, "AAA") == 0) {
        nBondType = 1;
      } else if (strcmp(pIssuerRating, "AA+") == 0) {
        nBondType = 2;
      } else {
        nBondType = 3;
      }
      break;
    }
    default:
      break;
  }

  return nBondType;
}

bool CPanoramicBondDealContainer::VerifyDealValid(
    const sPanoramicBondDealUnit& unit) {
  int nDealStatus = atoi(unit.m_dealStatus);
  if (nDealStatus >= 4) {
    return false;
  } else {
    return true;
  }
}

const BondDealInfo& CPanoramicBondDealContainer::getDealInfo(int bondIndex) {
  return m_bondDeal.getDealInfo(bondIndex);
}

void CPanoramicBondDealContainer::SetHistory() { m_isHistory = true; }

int CPanoramicBondDealContainer::GetDate(const char* cData) {
  if (!cData || IsStringEmpty(cData)) return 0;
  // ss::base::BaseString szTerm(cData, strlen(cData));
  // ss::base::BaseString szPlus = "+";
  QString szTerm(cData);
  QString szPlus = "+";
  int nPos = szTerm.indexOf(szPlus);
  if (nPos > 0) {
    szTerm = szTerm.mid(0, nPos);
  }
  int nOffSet = 1;
  int nTermLength = szTerm.length() /*strlen(szTerm.c_str())*/;
  // ss::base::BaseString szUnit = szTerm.substr(nTermLength - nOffSet, 1);
  // ss::base::BaseString szValue = szTerm.substr(0, nTermLength - nOffSet);
  QString szUnit = szTerm.mid(nTermLength - nOffSet, 1);
  QString szValue = szTerm.mid(0, nTermLength - nOffSet);
  while (true) {
    if (szUnit == "Y" || szUnit == "M" || szUnit == "D" ||
        nOffSet > nTermLength) {
      break;
    }
    ++nOffSet;
    szUnit = szTerm.mid(nTermLength - nOffSet, 1);
    szValue = szTerm.mid(0, nTermLength - nOffSet);
  }

  double fBase = szValue.toDouble();  // atof(szValue.c_str());
  if (szUnit == "Y") {
    return fBase * 365;
  } else if (szUnit == "M") {
    if ((abs(fBase - 12) < 0.0001)) return 365;
    return fBase * 30;
  } else if (szUnit == "D") {
    return fBase;
  } else {
    return 0;
  }
}

int CPanoramicBondDealContainer::GetBondDealInvalidateDay() {
  static int m_gMarketStreamToday = 0;
  static int m_gMarketStreamValidDate = 0;
  time_t t_Today =
      CSSVContainer::GetServerTime();  // AfxGetBondContainer().GetServerTime();
  int nToday = qb::base::IntDate::timeToInt(t_Today);
  if (nToday <= 19700101) return 0;
  if (nToday == m_gMarketStreamToday) {
    return m_gMarketStreamValidDate;
  }
  m_gMarketStreamToday = nToday;
  CBondHoliday bondHoliday = CBondCalendar::instance().GetBondHoliday();
  for (int i = 0; i < 100; i++) {
    qb::base::CTime Td(t_Today);
    int wd = Td.GetDayOfWeek();
    std::map<int, BondHolidayList>::const_iterator itr =
        bondHoliday.date.find(nToday);
    if (itr == bondHoliday.date.end()) {
      if (wd != 1 && wd != 7) {
        m_gMarketStreamValidDate = nToday;
        return nToday;
      }
      // t_Today -= 60 * 60 * 24;
    } else {
      bool bFind = false;
      for (std::vector<BondHoliday>::const_iterator itrH =
               itr->second.holidays.begin();
           itrH != itr->second.holidays.end(); ++itrH) {
        if (std::string(itrH->country) == std::string("CNY") &&
            std::string(itrH->market) == std::string("CIB")) {
          bFind = true;
          if (std::string(itrH->status) == std::string("T")) {
            m_gMarketStreamValidDate = nToday;
            return nToday;
          }
        }
      }
      if (!bFind && wd != 1 && wd != 7) {
        m_gMarketStreamValidDate = nToday;
        return nToday;
      }
    }
    t_Today -= 60 * 60 * 24;
    nToday = qb::base::IntDate::timeToInt(t_Today);
  }
  m_gMarketStreamValidDate = nToday;
  return nToday;
}

double CPanoramicBondDealContainer::FloatToDouble(const float& fValue) {
  auto CheckPriceValid = [](float value) { return value > DOUBLE_ZERO; };
  if (!CheckPriceValid(fValue)) return DOUBLE_INVALID;
  char sValue[32] = {0};
  FMTBUF(sValue, "%0.5f", fValue);
  return atof(sValue);
}

bool CPanoramicBondDealContainer::VerifyYieldValid(
    const sPanoramicBondDealUnit& unit) {
  double fDeal = atof(unit.m_yield);
  if (fDeal > DOUBLE_ZERO &&
      fDeal <
          29.99995) {  // 应产品要求，图表部分，收益率大于30不显示;修改QA提出的bug,不含30的收益率
    return true;
  }
  return false;
}

bool CPanoramicBondDealContainer::VerifyBondValid(
    const sPanoramicBondDealUnit& unit) {
  if (strcmp(unit.m_OptionType, "ETS") == 0 || unit.m_Redemption_No != 0) {
    return false;
  }
  return true;
}

void CPanoramicBondDealContainer::GetEstimatePrice(
    double& dEstPrice, double& dEstCleanPrice, const double& dDealYield,
    const double& dDealCleanPrice, int nBondIndex) {
  bool bIsCDCAuthValid = true;  // pBondInfo->IsCDCAuthValid();
  if (bIsCDCAuthValid) {
    CBondCDCPriceInfo& bcdc = CBondCDCPriceInfo::instance();
    int nOptTypeYield = CBondCDCPriceInfo::est_type_invalid;
    std::string strYield =
        bcdc.GetCDCPriceYesterday(nOptTypeYield, bIsCDCAuthValid, nBondIndex,
                                  CBondCDCPriceInfo::price_type_yield, false);
    int nOptTypeClean = CBondCDCPriceInfo::est_type_invalid;
    std::string strClean =
        bcdc.GetCDCPriceYesterday(nOptTypeClean, bIsCDCAuthValid, nBondIndex,
                                  CBondCDCPriceInfo::price_type_clean, false);
    if (nOptTypeYield != CBondCDCPriceInfo::est_type_invalid &&
        nOptTypeYield == nOptTypeClean) {
      switch (nOptTypeYield) {
        case CBondCDCPriceInfo::est_type_exercise:
        case CBondCDCPriceInfo::est_type_expire:
        case CBondCDCPriceInfo::est_type_null: {
          dEstPrice = GetDoubleValue(strYield.c_str());
          dEstCleanPrice = GetDoubleValue(strClean.c_str());
          break;
        }
        case CBondCDCPriceInfo::est_type_combine: {
          double dYieldLeft, dYieldRight, dCleanLeft, dCleanRight;
          dYieldLeft = dYieldRight = dCleanLeft = dCleanRight = DOUBLE_INVALID;

          for (int i = 0; i < 2; ++i) {
            size_t nSplit = std::string::npos;
            std::string sPrc;
            switch (i) {
              case 0: {
                sPrc = strYield;
              } break;
              case 1: {
                sPrc = strClean;
              } break;
              default:
                break;
            }
            nSplit = sPrc.find(bcdc.GetSplit());
            std::string sLeft = sPrc.substr(0, nSplit);
            std::string sRight =
                sPrc.substr(nSplit + 1, sPrc.length() - nSplit);
            if (sLeft.empty() || sRight.empty()) continue;
            switch (i) {
              case 0: {
                dYieldLeft = GetDoubleValue(sLeft.c_str());
                dYieldRight = GetDoubleValue(sRight.c_str());
              } break;
              case 1: {
                dCleanLeft = GetDoubleValue(sLeft.c_str());
                dCleanRight = GetDoubleValue(sRight.c_str());
              } break;
              default:
                break;
            }
          }

          if (!(CHECK_DOUBLE_VALID(dYieldLeft)) ||
              !(CHECK_DOUBLE_VALID(dYieldRight)) ||
              !(CHECK_DOUBLE_VALID(dCleanLeft)) ||
              !(CHECK_DOUBLE_VALID(dCleanRight)))
            break;
          // 计算净价和收益率偏离时，如果是有两个估值的债券，按照中债估值减两次，得到的结果中，优先取收益率和净价偏离正负相反的（0也算相反）。
          // 如果两个结果都是相反的，那么取收益率偏离绝对值最小的。

          double dGapYieldLeft = GetDoubleBP(dDealYield, dYieldLeft);
          double dGapCleanLeft = GetDoubleBP(dDealCleanPrice, dCleanLeft);
          double dGapYieldRight = GetDoubleBP(dDealYield, dYieldRight);
          double dGapCleanRight = GetDoubleBP(dDealCleanPrice, dCleanRight);
          bool bLeftDirectionDif =
              VerifyDirection(dGapYieldLeft, dGapCleanLeft);
          bool bRightDirectionDif =
              VerifyDirection(dGapYieldRight, dGapCleanRight);
          bool bLeft = true;
          if (bLeftDirectionDif == bRightDirectionDif) {
            // 方向相同，优先取绝对值
            if (fabs(dGapYieldLeft) > fabs(dGapYieldRight)) {
              bLeft = false;
            }
          } else if (!bLeftDirectionDif) {
            // 不处理，默认left
          } else {
            bLeft = false;
          }
          if (bLeft) {
            dEstPrice = dYieldLeft;
            dEstCleanPrice = dCleanLeft;
          } else {
            dEstPrice = dYieldRight;
            dEstCleanPrice = dCleanRight;
          }
          break;
        }
        default:
          break;
      }
    }
  }
}

bool CPanoramicBondDealContainer::IsStringEmpty(const char* pStr) {
  if (!pStr) return true;
  if (*pStr == '\0') return true;
  return false;
}
